# Retain for backward compatibility.  Methods are now included in Class.
module ClassInheritableAttributes # :nodoc:
end

# Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
# their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
# to, for example, an array without those additions being shared with either their parent, siblings, or
# children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.

# 允许属性和继承体系共享,但是不直接指向同一个对象,而是取得属性的一份拷贝.
# 这个现象不同于正常情况下的继承体系.
# changed by bonyfish choi on 2009-03-31

# 举例说明,注意到最后几句b操作的已经不是实例变量@a了,而是不同的拷贝
# class A;attr_accessor :a;end # => nil
# class B<A;end # => nil
# b=B.new # => #<B:0xb7f010b0>
# b.a=33 # => 33
# b # => #<B:0xb7f010b0 @a=33>
# class B<A;class_inheritable_accessor("a");end # => ["a"]
# b.a=44 # => 44
# b # => #<B:0xb7f010b0 @a=33>
# b.a # => 44

class Class # :nodoc:
  def class_inheritable_reader(*syms)
    syms.each do |sym|
      next if sym.is_a?(Hash)
      class_eval <<-EOS
        def self.#{sym}
          read_inheritable_attribute(:#{sym})
        end

        def #{sym}
          self.class.#{sym}
        end
      EOS
    end
  end

  def class_inheritable_writer(*syms)
    options = syms.extract_options!
    syms.each do |sym|
      class_eval <<-EOS
        def self.#{sym}=(obj)
          write_inheritable_attribute(:#{sym}, obj)
        end

        #{"
      def #{sym}=(obj)
          self.class.#{sym} = obj
        end
        " unless options[:instance_writer] == false }
      EOS
    end
  end

  def class_inheritable_array_writer(*syms)
    options = syms.extract_options!
    syms.each do |sym|
      class_eval <<-EOS
        def self.#{sym}=(obj)
          write_inheritable_array(:#{sym}, obj)
        end

        #{"
      def #{sym}=(obj)
          self.class.#{sym} = obj
        end
        " unless options[:instance_writer] == false }
      EOS
    end
  end

  def class_inheritable_hash_writer(*syms)
    options = syms.extract_options!
    syms.each do |sym|
      class_eval <<-EOS
        def self.#{sym}=(obj)
          write_inheritable_hash(:#{sym}, obj)
        end

        #{"
      def #{sym}=(obj)
          self.class.#{sym} = obj
        end
        " unless options[:instance_writer] == false }
      EOS
    end
  end

  def class_inheritable_accessor(*syms)
    class_inheritable_reader(*syms)
    class_inheritable_writer(*syms)
  end

  def class_inheritable_array(*syms)
    class_inheritable_reader(*syms)
    class_inheritable_array_writer(*syms)
  end

  def class_inheritable_hash(*syms)
    class_inheritable_reader(*syms)
    class_inheritable_hash_writer(*syms)
  end

  def inheritable_attributes
    @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
  end

  def write_inheritable_attribute(key, value)
    if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
      @inheritable_attributes = {}
    end
    inheritable_attributes[key] = value
  end

  def write_inheritable_array(key, elements)
    write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
    write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
  end

  def write_inheritable_hash(key, hash)
    write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
    write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
  end

  def read_inheritable_attribute(key)
    inheritable_attributes[key]
  end

  def reset_inheritable_attributes
    @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
  end

  private
  # Prevent this constant from being created multiple times
  # 这是个常量,可以通过const_defined?判断是否存在
  EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)

  def inherited_with_inheritable_attributes(child)
    inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)

    if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
      new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
    else
      new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
        memo.update(key => value.duplicable? ? value.dup : value)
      end
    end

    child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
  end


  # inherited是Class的一个私有方法,当类的某个子类被创建的时候,将会回调这个方法
  # 这里对inherited进行重新命名,这样当继承一个类的时候,实际会触发的就是inherited_with_inheritable_attributes
  #Example:
  #
  #   class Foo
  #      def self.inherited(subclass)
  #         puts "New subclass: #{subclass}"
  #      end
  #   end
  #
  #   class Bar < Foo
  #   end
  #
  #   class Baz < Bar
  #   end
  #
  #produces:
  #
  #   New subclass: Bar
  #   New subclass: Baz

  alias inherited_without_inheritable_attributes inherited
  alias inherited inherited_with_inheritable_attributes
end
